/**
 * @file std_c_redirect.c
 * @author zhaitao (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2020-06-24
 *
 * @copyright Copyright (c) 2020
 *
 * https://interrupt.memfault.com/blog/boostrapping-libc-with-newlib
 * https://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html
 * https://blog.csdn.net/ZLK1214/article/details/86634677
 * https://github.com/RIOT-OS/RIOT/blob/master/pkg/fatfs/fatfs_vfs/fatfs_vfs.c
 * http://www.nc.es.ncku.edu.tw/course/embedded/09/
 */

#include "ff.h"
#include "main.h"
#include "math.h"
#include "stdlib.h"
#include "string.h"
#include <_ansi.h>
#include <errno.h>
#include <reent.h>
#include <stdio.h>
#include <string.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define RETARGET_DEBUG 1
#define MAX_STACK_SIZE 0x4000

extern UART_HandleTypeDef huart1;

FATFS r_fs;
FIL r_fil;
FRESULT r_fr;
uint32_t r_br;

//#pragma import(__use_no_semihosting)
static int fatfs_err_to_errno(int32_t err);

//#define FreeRTOS

extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));

#ifndef FreeRTOS
register char *stack_ptr asm("sp");
#endif

caddr_t _sbrk(int incr)
{
#if 1
	extern char end asm("end");
	static char *heap_end;
	char *prev_heap_end;
#ifdef FreeRTOS
	char *min_stack_ptr;
#endif

	if (heap_end == 0)
		heap_end = &end;

	prev_heap_end = heap_end;

	// printf("in _lseek %d %d %d\r\n", (int)heap_end, (int)prev_heap_end, incr);

#ifdef FreeRTOS
	/* Use the NVIC offset register to locate the main stack pointer. */
	min_stack_ptr = (char *)(*(unsigned int *)*(unsigned int *)0xE000ED08);
	/* Locate the STACK bottom address */
	min_stack_ptr -= MAX_STACK_SIZE;

	if (heap_end + incr > min_stack_ptr)
#else
	if (heap_end + incr > stack_ptr)
#endif
	{
		//		write(1, "Heap and stack collision\n", 25);
		//		abort();
		errno = ENOMEM;
		return (caddr_t)-1;
	}

	heap_end += incr;

	return (caddr_t)prev_heap_end;
#endif
}

/*
 * _gettimeofday primitive (Stub function)
 * */
int _gettimeofday(struct timeval *tp, struct timezone *tzp)
{
	/* Return fixed data for the timezone.  */
	if (tzp) {
		tzp->tz_minuteswest = 0;
		tzp->tz_dsttime = 0;
	}

	return 0;
}
void initialise_monitor_handles()
{
}

int _getpid(void)
{
	return 1;
}

int _kill(int pid, int sig)
{
	errno = EINVAL;
	return -1;
}

void _exit(int status)
{
	_kill(status, -1);
	while (1) {
	}
}

int _write(int fd, char *str, int len)
{
	for (int i = 0; i < len; i++) {
		HAL_UART_Transmit(&huart1, (uint8_t *)&str[i], 1, 0xff);
	}
	return len;
}

int _close(int file)
{
	r_fr = f_close(&r_fil);
	if (r_fr) {
		printf("f_close error\r\n");
		return -1;
	}

	return 0;
}

int _fstat(int file, struct stat *st)
{
	st->st_mode = S_IFCHR;
	return 0;
}

int _isatty(int fd)
{
	if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
		return 1;

	errno = EBADF;
	return 0;
}

int _lseek(int file, int ptr, int dir)
{
	printf("in _lseek %d %d %d\r\n", file, ptr, dir);

	off_t new_pos = 0;

	if (dir == SEEK_SET) {
		new_pos = ptr;
	} else if (dir == SEEK_CUR) {
		new_pos = f_tell(&r_fil) + ptr;
	} else if (dir == SEEK_END) {
		new_pos = f_size(&r_fil) + ptr;
	} else {
		return fatfs_err_to_errno(FR_INVALID_PARAMETER);
	}

	r_fr = f_lseek(&r_fil, new_pos);

	return fatfs_err_to_errno(r_fr);
}

int _read(int file, char *ptr, int len)
{
	r_fr = f_read(&r_fil, ptr, len, (UINT *)&r_br);

	if (r_fr) {
		return fatfs_err_to_errno(r_fr);
	}

	return r_br;
}

int _open(char *path, int flags, ...)
{
	r_fr = f_open(&r_fil, path, FA_READ | FA_OPEN_EXISTING);

	return fatfs_err_to_errno(r_fr);
}

int _wait(int *status)
{
	errno = ECHILD;
	return -1;
}

int _unlink(char *name)
{
	errno = ENOENT;
	return -1;
}

int _times(struct tms *buf)
{
	return -1;
}

int _stat(char *file, struct stat *st)
{
	st->st_mode = S_IFCHR;
	return 0;
}

int _link(char *old, char *new)
{
	errno = EMLINK;
	return -1;
}

int _fork(void)
{
	errno = EAGAIN;
	return -1;
}

int _execve(char *name, char **argv, char **env)
{
	errno = ENOMEM;
	return -1;
}

static int fatfs_err_to_errno(int32_t err)
{
	switch (err) {
	case FR_OK:
		return 0;
	case FR_DISK_ERR:
		return -EIO;
	case FR_INT_ERR:
		return -EIO;
	case FR_NOT_READY:
		return -ENODEV;
	case FR_NO_FILE:
		return -ENOENT;
	case FR_NO_PATH:
		return -ENOENT;
	case FR_INVALID_NAME:
		return -ENOENT;
	case FR_DENIED:
		return -EACCES;
	case FR_EXIST:
		return -EEXIST;
	case FR_INVALID_OBJECT:
#ifdef EBADFD
		return -EBADFD;
#else
		return -EINVAL;
#endif
	case FR_WRITE_PROTECTED:
		return -EACCES;
	case FR_INVALID_DRIVE:
		return -ENXIO;
	case FR_NOT_ENABLED:
		return -ENODEV;
	case FR_NO_FILESYSTEM:
		return -ENODEV;
	case FR_MKFS_ABORTED:
		return -EINVAL;
	case FR_TIMEOUT:
		return -EBUSY;
	case FR_LOCKED:
		return -EACCES;
	case FR_NOT_ENOUGH_CORE:
		return -ENOMEM;
	case FR_TOO_MANY_OPEN_FILES:
		return -ENFILE;
	case FR_INVALID_PARAMETER:
		return -EINVAL;
	}

	return (int)err;
}
